package dispatcher

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/managers/taskmanager"
	"dev.ixmxm.top/rms/model"
	"dev.ixmxm.top/rms/proto"
	"dev.ixmxm.top/rms/utils/comm"
	"dev.ixmxm.top/rms/utils/json"
	"dev.ixmxm.top/rms/utils/log"
	"dev.ixmxm.top/rms/utils/tools"
)

func newRobotCaller(account model.RobotAccount, statusCallback func(status *model.RobotStatus)) *robotCaller {
	rc := &robotCaller{account: account, statusCallback: statusCallback}
	rc.Start()
	return rc
}

type robotCaller struct {
	account        model.RobotAccount
	client         proto.RobotServiceClient
	statusCallback func(status *model.RobotStatus)
}

//启动小车连接
func (rc *robotCaller) Start() {
	for {
		conn, err := comm.GRPCClient(fmt.Sprintf("%s:%d", rc.account.AgvIP, rc.account.AgvPort))
		if err != nil {
			log.Errorf("dail robot:[%s] grpc error:%s", rc.account.AgvIP, err.Error())
			tools.Sleep(3.0)
			continue
		}
		rc.client = proto.NewRobotServiceClient(conn)
		return
	}
}

//向AGV下发断电任务
func (rc *robotCaller) DischargeTask(ctx context.Context) codes.Code {
	if rc.client == nil {
		log.Errorf("robot caller is nil")
		return codes.ErrCallService
	}
	globalPathsPb := make([]*proto.PathPb, 0)
	localPathsPb := make([]*proto.PathPb, 0)

	log.Infof("robot:%s 开始给下发断电任务", rc.account.AgvId)
	res, err := rc.client.RunTask(ctx, &proto.RunTaskRequest{
		GlobalPaths:     globalPathsPb,
		LocalPaths:      localPathsPb,
		TaskId:          1,
		TaskType:        proto.TaskTypePb(7),
		TargetSiteId:    uint32(10000),
		TargetId:        uint32(10000),
		CheckType:       0,
		FinishCheck:     1,
		TargetLayer:     0,
		TargetDirection: 0,
		//ForkType:        0,
	})
	if err != nil {
		log.Errorf("下发断电任务失败 robot:%s ，error:%s", rc.account.AgvId, err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	if res.GetCode() != 0 {
		log.Errorf("send task to robot error:%s", res.GetMessage())
		return codes.ErrCallService.WithDetails(res.GetMessage())
	}
	log.Infof("robot:%s receive task reply:%s", rc.account.AgvId, json.DumpString(res))
	return nil
}

//下发任务
func (rc *robotCaller) RunTask(ctx context.Context, subtask *model.Subtask, globalPaths *model.GlobalPath, localPaths *model.LocalPath, robot *model.Robot, m *DispatchManager) codes.Code {
	SetUnCancel := func(agvID, subFaildTaskId string) codes.Code {
		//先判断请求上线车是否存在取消中和完成中
		intNum, _ := strconv.Atoi(subFaildTaskId)
		subFaildTaskId1 := uint64(intNum)
		runningSubtask, _ := m.mc.TM.GetSubtaskFromDB(ctx, subFaildTaskId1)
		// if err1 != nil {
		// 	return err1
		// }
		if runningSubtask == nil || runningSubtask.TaskOperateFlg == 1 {
			caller, _ := m.GetCaller(agvID)
			if caller == nil {
				err := codes.ErrRobotOperate.WithDetails("can not find robot caller")
				return err
			}

			if err2 := caller.OperateTask(ctx, subFaildTaskId1, m.mc.TM, proto.TaskOperatePb_TaskCancel); err2 != nil {
				log.ErrorfWithContext(ctx, "operate task error:%s", err2.UnWrapError())
				return err2
			}
		}
		return nil
	}
	if rc.client == nil {
		log.Errorf("robot caller is nil")
		return codes.ErrCallService
	}
	globalPathsPb := make([]*proto.PathPb, 0)
	localPathsPb := make([]*proto.PathPb, 0)
	tools.Copy(globalPaths.Paths, &globalPathsPb)
	tools.Copy(localPaths.Paths, &localPathsPb)

	// if subtask.Type == model.TaskTypeFork {
	// 	log.Infof("robot:%s 开始给下发货叉任务(%d) 货叉动作类型:%d(%d--%s)", rc.account.AgvId, subtask.Id, int32(subtask.ForkActionType), subtask.ForkActionType, subtask.ForkActionType)
	// }
	// log.Warnf("robot:%s 下发任务(%d) 临时目标点%s 目标层%d WalkType:%d 局部路径：%v", rc.account.AgvId, subtask.Id, subtask.TempTargetSite, subtask.TargetLayer, subtask.WalkType, localPaths.Paths.ToIDs())
	//限制下发任务为同一条任务，才启用货叉完成记录
	var forkTaskIndex int32 = 0
	if robot.StatusIsExists() && robot.Status.ForkTaskIndex > 0 && subtask.Id == robot.Status.TaskId {
		forkTaskIndex = robot.Status.ForkTaskIndex
	}
	log.Warnf("robot:%s 下发任务(%d) 临时目标点%s (主任务目标点：%s 货叉上报：%d 检测：%d)目标层%d WalkType:%d 局部路径：%v 货叉动作：%v", rc.account.AgvId, subtask.Id, subtask.TempTargetSite, subtask.MainTaskTargetSiteID, subtask.IsForkReport, subtask.CheckType, subtask.TargetLayer, subtask.WalkType, localPaths.Paths.ToIDs(), subtask.GetForkActions(forkTaskIndex))
	target, _ := strconv.Atoi(subtask.TargetSiteID)
	mianTaskTarget, _ := strconv.Atoi(subtask.MainTaskTargetSiteID)

	// mianTaskTarget, _ := strconv.Atoi(subtask.MainTaskTargetSiteID)
	res, err := rc.client.RunTask(ctx, &proto.RunTaskRequest{
		GlobalPaths:     globalPathsPb,
		LocalPaths:      localPathsPb,
		TaskId:          subtask.Id,
		TaskType:        proto.TaskTypePb(subtask.Type),
		TargetSiteId:    uint32(target),
		TargetId:        uint32(mianTaskTarget),
		FinishCheck:     uint32(subtask.IsForkReport),
		CheckType:       uint32(subtask.CheckType),
		TargetLayer:     subtask.TargetLayer,
		TargetDirection: subtask.TargetDirection,
		WalkType:        subtask.WalkType,
		ForkAction:      subtask.GetForkActions(forkTaskIndex),
		DetectionType:   uint32(subtask.PalletDetection),
		//IsTransmit:      !globalPaths.IsFinalGoal(),
	})
	if err != nil {
		log.Errorf("下发任务(%d)和路径信息失败 robot:%s ，error:%s", subtask.Id, rc.account.AgvId, err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	if res.GetCode() != 0 {
		log.Errorf("send task to robot:%s error 错误码:%d 错误信息：%s", rc.account.AgvId, res.GetCode(), res.GetMessage())
		//特殊说明：返回100说明小车必须重新上线，才可下发任务
		if res.GetCode() == 100 {
			log.Errorf("下发任务给robot:%s出错,错误码:%d,更新任务在线为false", rc.account.AgvId, res.GetCode())
			robot.UpdateTaskOnlineStatus(false)
		} else if res.GetCode() == 200 {
			//找到正在当前车正在取消中的任务
			err1 := SetUnCancel(robot.Account.AgvId, res.GetMessage())
			if err1 != nil {
				return err1
			}
		}
		return codes.ErrCallService.WithDetails(res.GetMessage())
	}
	// log.Infof("robot:%s receive task:%d,reply:%s", rc.account.AgvId, subtask.Id, json.DumpString(res))
	return nil
}

//地图切换
func (rc *robotCaller) SwitchMap(ctx context.Context, project_id, traffic_name, site_id, path_id string) codes.Code {
	site, _ := strconv.Atoi(site_id)
	path, _ := strconv.Atoi(path_id)

	reply, err := rc.client.SetAreaAndFloor(ctx, &proto.AreaAndFloorRequest{ProjectId: project_id, TrafficMapName: traffic_name, CurrentId: int32(site), PathId: int32(path)})
	if err != nil {
		log.Errorf("switch robot map from grpc error:%s", err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	if reply.Code == 1 {
		log.Errorf("switch robot map from grpc error:%s", reply.Message)
		return codes.ErrCallService
	}
	return nil
}

//下发地图
func (rc *robotCaller) SendMaps(ctx context.Context, maps []*model.Map) codes.Code {
	if len(maps) == 0 {
		return nil
	}
	// log.Infof("send maps to robot:%s", rc.account.AgvIP)
	mapPbs := make([]*proto.MapPb, 0)
	for _, m := range maps {
		mapPbs = append(mapPbs, &proto.MapPb{ProjectId: m.ProjectId, TrafficMapName: m.TrafficName, Data: m.RawBase64.String()})
	}
	_, err := rc.client.ReceiveMaps(ctx, &proto.ReceiveMapsRequest{Maps: mapPbs})
	if err != nil {
		log.Errorf("start send robot maps from grpc stream error:%s", err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	return nil
}

//任务操作
func (rc *robotCaller) OperateTask(ctx context.Context, taskID uint64, tm *taskmanager.TaskManager, code proto.TaskOperatePb) codes.Code {
	log.Infof("给小车(%s)下发任务(%d)操作(%s)", rc.account.AgvId, taskID, code.String())
	// runningSubtask, err0 := tm.GetSubtaskFromDB(ctx, taskID)
	// if err0 != nil {
	// 	log.Infof("给小车(%s)下发任务(%d)不存在", rc.account.AgvId, taskID)
	// 	return err0
	// } else if time.Since(runningSubtask.StartTime) < time.Second {
	// 	log.Infof("给小车(%s)下发任务(%d)时间短，不允许操作", rc.account.AgvId, taskID)
	// 	return codes.ErrCallService
	// }
	newCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	tm.UpdateSubtaskKeyToDB(ctx, taskID, "task_operate_flag", 1)
	defer cancel()
	_, err := rc.client.TaskOperate(newCtx, &proto.TaskRequest{TaskId: taskID, OperateCmd: code, Version: proto.RobotReqVersion})
	if err != nil {
		log.Errorf("给小车(%s)下发任务(%d)操作(%s)失败 error:%s", rc.account.AgvId, taskID, code.String(), err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	return nil
}

//发送锁点错误
func (rc *robotCaller) SendBlockConflictAlarm(ctx context.Context, site int) codes.Code {
	log.Warnf("告诉小车:%s ,需要重新上线", rc.account.AgvId)
	_, err := rc.client.SendLockPointError(ctx, &proto.RequestLockPointError{AgvId: rc.account.AgvId, SiteId: int32(site)})
	if err != nil {
		log.Errorf("send robot block conflict alarm error:%s", err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	return nil
}

//获取小车的外接设备信息  sensorType：设备类型 0:RFID 1:货物状态
func (rc *robotCaller) GetSensorData(ctx context.Context, sensorType int32) (*proto.SensorData, codes.Code) {
	log.Infof("获取小车(%s)的外接设备(设备类型：%d)信息", rc.account.AgvId, sensorType)
	newCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	sensorData, err := rc.client.GetSensorData(newCtx, &proto.RequestSensor{SensorType: sensorType})
	if err != nil {
		log.Errorf("获取小车(%s)的外接设备(设备类型：%d)信息失败 error:%s", rc.account.AgvId, sensorType, err.Error())
		return nil, codes.ErrCallService.Wrap(err)
	}
	return sensorData, nil
}

//获取小车的外接设备信息  sensorType：设备类型 0:RFID 1:货物状态
func (rc *robotCaller) SetSensorData(ctx context.Context, sensorType int32, data string) (*proto.SensorData, codes.Code) {
	log.Infof("设置小车(%s)的外接设备(设备类型：%d)信息", rc.account.AgvId, sensorType)
	newCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	sensorData, err := rc.client.GetSensorData(newCtx, &proto.RequestSensor{SensorType: sensorType, CmdData: data})
	if err != nil {
		log.Errorf("获取小车(%s)的外接设备(设备类型：%d)信息失败 error:%s", rc.account.AgvId, sensorType, err.Error())
		return nil, codes.ErrCallService.Wrap(err)
	}
	return sensorData, nil
}

//推送小车配置
func (rc *robotCaller) SendConfig(ctx context.Context, robotType *model.RobotType) codes.Code {
	config := &proto.RequestSendConfig{
		CategoryId:            int32(robotType.CategoryId),
		CategoryType:          int32(robotType.CategoryType),
		Length:                int32(robotType.Length),
		Width:                 int32(robotType.Width),
		MaxVel:                int32(robotType.MaxVel),
		MaxAng:                int32(robotType.MaxAng),
		RotateTime:            int32(robotType.RotateTime),
		StopTime:              int32(robotType.StopTime),
		ChargePercent:         int32(robotType.ChargePercent),
		LowPowerFinishPercent: int32(robotType.ChargeToTaskPercent),
		LocalPathNodeNum:      int32(robotType.LocalPathNodeNum),
		LocalPathDis:          int32(robotType.LocalPathDis),
		WalkSafeHeight:        robotType.WalkSafeHeight,
	}
	_, err := rc.client.SendConfig(ctx, config)
	if err != nil {
		log.Errorf("发送配置信息到robot%s error:%s", rc.account.AgvId, err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	log.Infof("发送配置信息 robot%s", rc.account.AgvId)
	return nil
}

//下发小车软件急停
func (rc *robotCaller) SoftStop(ctx context.Context, isStop bool) codes.Code {
	var flag uint32 = 0
	if isStop {
		flag = 1
	}
	res, err := rc.client.SoftStop(ctx, &proto.RequestStop{
		SoftStop: flag,
	})
	if err != nil {
		log.Errorf("发送小车软件急停信息到robot%s error:%s", rc.account.AgvId, err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	if res.Code != 0 {
		log.Errorf("发送小车软件急停信息到robot%s error:%s", rc.account.AgvId, res.Message)
		return codes.ErrCallService.WithDetails(res.Message)
	}
	log.Infof("发送小车软件急停信息 robot%s", rc.account.AgvId)
	return nil
}

//下发报错信息
func (rc *robotCaller) SendErrToRobot(ctx context.Context, errCode int, errLevel, errMsg string) codes.Code {
	reply, err := rc.client.Error(ctx, &proto.ErrorRequest{
		ErrorCode:     "",
		ErrorLevel:    0,
		ErrorDescribe: "",
		ErrorSolution: "",
	})
	if err != nil {
		log.Errorf("send err msg to robot from grpc error:%s", err.Error())
		return codes.ErrCallService.Wrap(err)
	}
	if reply.Code == 1 {
		log.Errorf("send err msg to robot from grpc error:%s", reply.Message)
		return codes.ErrCallService
	}
	return nil
}
